Reaction API

API calls used by AbstractReaction implementations.

Implementations should create a subclass of AbstractReaction containing a ReactionBase and Parameters, optionally provide a create_reaction, and implement callbacks to define VariableReactions and register ReactionMethods.

Reaction struct

PALEOboxes.AbstractReactionType
AbstractReaction

Abstract base Type for Reactions.

Implementation

Derived types should include a field base::ReactionBase, and usually a ParametersTuple, eg

Base.@kwdef mutable struct ReactionHello{P} <: PB.AbstractReaction
    base::PB.ReactionBase

    pars::P = PB.ParametersTuple(
        PB.ParDouble("my_par", 42.0, units="yr", 
            description="an example of a Float64-valued scalar parameter called 'my_par'"),
    )

    some_additional_field::Float64   # additional fields to eg cache data read from disk etc
end

Derived types should implement register_methods!, and may optionally implement create_reaction, set_model_geometry, check_configuration, register_dynamic_methods!.

Methods should be registered using add_method_setup!, add_method_initialize!, add_method_do!.

Any parameters not included in pars should be added explicitly with add_par (this is rarely needed).

source

Parameters

PALEOboxes.ParameterType
Parameter{T, ParseFromString}

A reaction parameter of type T.

Create using short names ParDouble, ParInt, ParBool, ParString.

Read value as <par>[], set with setvalue!.

Parameters with external=true may be set from the Model-level Parameters list, if name is present in that list.

ParseFromString should usually be Nothing: a value of Type T is then required when calling setvalue!. If ParseFromString is not Nothing, then setvalue! will accept an AbstractString and call Base.parse(ParseFromString, strvalue). This allows eg an enum-valued Parameter to be defined by Parameter{EnumType, EnumType} and implementing parse(EnumType, rawvalue::AbstractString)

source
PALEOboxes.VecParameterType
VecParameter{T, ParseFromString}

A reaction parameter of type Vector{T}.

Create using short names ParDoubleVec, ParStringVec.

Read values as <par>[i], access raw Vector{T} as <par>.v.

Set with setvalue!, in config file use standard yaml syntax for a vector eg [1, 2, 3]

See Parameter for additional documentation.

Implementation

Implements part of the AbstractVector interface, sufficient to access elements as <par>[i], and to support iteration eg for v in <par>; ...; end.

source
PALEOboxes.VecVecParameterType
VecVecParameter{T, ParseFromString}

A reaction parameter of type Vector{Vector{T}}.

Create using short names ParDoubleVecVec.

Read values as <par>.v::Vector{Vector{T}}.

Set using standard yaml syntax for a vector of vectors eg [[1, 2, 3], [4, 5, 6]]

See Parameter for additional documentation.

source
PALEOboxes.ParametersTupleFunction
ParametersTuple(parameters::AbstractParameter...) -> NamedTuple
ParametersTuple(parameters) -> NamedTuple

Create a NamedTuple of Parameters.

source
PALEOboxes.add_parFunction
add_par(reaction::AbstractReaction, par::AbstractParameter)
add_par(reaction::AbstractReaction, objectwithpars)

Add a single parameter or parameters from fields of objectwithpars to a new Reaction.

Not usually needed: Parameters in pars::ParametersTuplewill be added automatically, only needed if there are additional Parameters that are not members ofpars`.

source
PALEOboxes.setvalue!Function
setvalue!(par::Parameter, value)

Set Parameter to value.

Optionally (if Parameter has Type parameter ParseFromString != Nothing) parse value from a String.

source

Registering Reactions with the PALEOboxes framework

All subtypes of AbstractReaction that are present in loaded modules are available to the PALEO framework. Available Reactions can be listed with find_reaction and find_all_reactions. The default create_reaction is called to create Reactions when the model is created (this method can be overridden if needed).

PALEOboxes.create_reactionMethod
create_reaction(ReactionType::Type{<:AbstractReaction}, base::ReactionBase) -> reaction::AbstractReaction

Default method to create a ReactionType and set base field.

A reaction implementation may optionally implement a custom method eg to set additional fields

source
PALEOboxes.find_all_reactionsFunction
find_all_reactions() -> Dict{String, Type}

Use InteractiveUtils.subtypes(AbstractReaction) to find all currently loaded subtypes off AbstractReaction, and create a Dict with last part of the name of the Type as key (ie without the module prefix) and Type as value.

Any Types that generate non-unique keys (eg Module1.MyReactionType and Module2.MyReactionType) will generate a warning, and no entry will be added to the Dict (so if this Reaction is present in a config file, it will not be found and will error).

source

Defining Domain Grids and array sizes

PALEOboxes.set_data_dimension!Function
set_data_dimension!(domain::Domain, dim::NamedDimension; allow_exists=false)

Define a Domain data dimension as a NamedDimension

Variables may then specify data dimensions as a list of names using the :data_dims Variable Attribute.

source

Creating and registering Reaction methods

All Reactions should implement register_methods!, and may optionally implement register_dynamic_methods!.

These methods should then define one or more ReactionMethods, which requires:

In addition it is possible to add Predefined ReactionMethods for some common operations (Variable initialisation, calculating totals, etc).

Defining VariableReactions

PALEOboxes.VariableReactionType
VariableReaction(VT, localname => link_namestr, units, description; attributes=Tuple()) -> VariableReaction{VT}
VariableReaction(VT, linklocal_namestr, units, description; attributes=Tuple()) -> VariableReaction{VT}    

VarProp, VarPropScalar, VarPropStateIndep, VarPropScalarStateIndep -> VariableReaction{VT_ReactProperty}
VarDep, VarDepColumn, VarDepScalar, VarDepStateIndep, VarDepColumnStateIndep, VarDepScalarStateIndep -> VariableReaction{VT_ReactDependency}
VarTarget, VarTargetScalar -> VariableReaction{VT_ReactTarget}
VarContrib, VarContribColumn, VarContribScalar -> VariableReaction{VT_ReactContributor}
VarStateExplicit, VarStateExplicitScalar -> VariableReaction{VT_ReactDependency}
VarDeriv, VarDerivScalar -> VariableReaction{VT_ReactContributor}
VarState, VarStateScalar -> VariableReaction{VT_ReactDependency}
VarConstraint, VarConstraintScalar -> VariableReaction{VT_ReactDependency}

[deprecated] VariableReaction(VT, localname, units, description; link_namestr, attributes=Tuple()) -> VariableReaction{VT}

Reaction view on a model variable.

Reactions define AbstractVarLists of VariableReactions when creating a ReactionMethod. Within a ReactionMethod, a VariableReaction is referred to by localname. When the model is initialised, VariableDomain variables are created that link together VariableReactions with the same link_namestr, and data Arrays are allocated. views on the VariableDomain data Arrays are then passed to the ReactionMethod function at each timestep.

Subtypes

The Type parameter VT is one of VT_ReactProperty, VT_ReactDependency, VT_ReactContributor, VT_ReactTarget, where short names are defined for convenience:

const VarPropT        = VariableReaction{VT_ReactProperty}
const VarDepT         = VariableReaction{VT_ReactDependency}
const VarTargetT      = VariableReaction{VT_ReactTarget}
const VarContribT     = VariableReaction{VT_ReactContributor}

There are two pairings of VariableReactions with VariableDomains:

  • Reaction Property and Dependency Variables, linked to a VariableDomPropDep. These are used to represent a quantity calculated in one Reaction that is then used by other Reactions.
  • Reaction Target and Contributor Variables, linked to a VariableDomContribTarget. These are used to represent a flux-like quantity, with one Reaction definining the Target and multiple Reactions adding contributions.

Variable Attributes and constructor convenience functions

Variable attributes are used to define Variable :space AbstractSpace (scalar, per-cell, etc) and data content :field_data AbstractData, and to label state Variables for use by numerical solvers.

VariableReaction is usually not called directly, instead convenience functions are defined that provide commonly-used combinations of VT and attributes:

short nameVTattributes
:space:field_data:vfunction:initialize_to_zero:datatype:is_constant
VarPropVT_ReactPropertyCellSpaceScalarDataVF_Undefined--false
VarPropScalarVT_ReactPropertyScalarSpaceScalarDataVF_Undefined--false
VarPropStateIndepVT_ReactPropertyCellSpaceScalarDataVF_Undefined-Float64true
VarPropScalarStateIndepVT_ReactPropertyScalarSpaceScalarDataVF_Undefined-Float64true
VarDepVT_ReactDependencyCellSpaceUndefinedDataVF_Undefined--false
VarDepColumnVT_ReactDependencyColumnSpaceUndefinedDataVF_Undefined--false
VarDepScalarVT_ReactDependencyScalarSpaceUndefinedDataVF_Undefined--false
VarDepStateIndepVT_ReactDependencyCellSpaceUndefinedDataVF_Undefined-Float64true
VarDepColumnStateIndepVT_ReactDependencyColumnSpaceUndefinedDataVF_Undefined-Float64true
VarDepScalarStateIndepVT_ReactDependencyScalarSpaceUndefinedDataVF_Undefined-Float64true
VarTargetVT_ReactTargetCellSpaceScalarDataVF_Undefinedtrue-false
VarTargetScalarVT_ReactTargetScalarSpaceScalarDataVF_Undefinedtrue-false
VarContribVT_ReactContributorCellSpaceUndefinedDataVF_Undefined--false
VarContribColumnVT_ReactContributorColumnSpaceUndefinedDataVF_Undefined--false
VarContribScalarVT_ReactContributorScalarSpaceUndefinedDataVF_Undefined--false
VarStateExplicitVT_ReactDependencyCellSpaceScalarDataVF_StateExplicit--false
VarStateExplicitScalarVT_ReactDependencyScalarSpaceScalarDataVF_StateExplicit--false
VarDerivVT_ReactContributorCellSpaceScalarDataVF_Derivtrue-false
VarDerivScalarVT_ReactContributorScalarSpaceScalarDataVF_Derivtrue-false
VarStateVT_ReactDependencyCellSpaceScalarDataVF_State--false
VarStateScalarVT_ReactDependencyScalarSpaceScalarDataVF_State--false
VarConstraintVT_ReactContributorCellSpaceScalarDataVF_Constrainttrue-false
VarConstraintScalarVT_ReactContributorScalarSpaceScalarDataVF_Constrainttrue-false

This illustrates some general principles for the use of attributes:

  • All Variables must define the :space VariableAttribute (a subtype of AbstractSpace) to specify whether they are Domain scalars, per-cell quantities, etc. This is used to define array dimensions, and to check that dimensions match when variables are linked.
  • The :field_data attribute (a subtype of AbstractData) specifies the quantity that Property and Target Variables represent. This defaults to ScalarData to represent a scalar value. To eg represent a single isotope the :field_data attribute should be set to IsotopeLinear. Dependency and Contributor Variables with :field_data = UndefinedData then acquire this value when they are linked, or may specify :field_data to constrain allowed links.
  • The :initialize_to_zero attribute is set for Target variables, this is than used (by the ReactionMethod created by add_method_initialize_zero_vars_default!) to identify variables that should be initialised to zero at the start of each timestep.
  • The :vfunction attribute is used to label state Variables and corresponding time derivatives, for access by a numerical solver.
    • An ODE-like combination of a state variable and time derivative are defined by a paired VarStateExplicit and VarDeriv. Note that that these are just VarDep and VarContrib with the :vfunction attribute set, and that there is no VarProp and VarTarget defined in the model (these are effectively provided by the numerical solver). The pairing is defined by the naming convention of varname and varname_sms.
    • An algebraic constraint (for a DAE) is defined by a VarState and VarConstraint. Note that that these are just VarDep and VarContrib with the :vfunction attribute set, and that there is no VarProp and VarTarget defined in the model (these are effectively provided by the numerical solver). These variables are not paired.
    • The :initializetozero attribute is also set for Contributor variables VarDeriv and VarConstraint (as there is no corresponding Target variable in the model).
    • The :field_data attribute should be set on labelled state etc Variables (as there are no corresponding Property or Target variables in the model to define this).
  • The :is_constant attribute is used to identify constant Property Variables (not modified after initialisation). A Dependency Variable with :is_constant = true can only link to a constant Property Variable.
  • The :datatype attribute is used both to provide a concrete datatype for constant Variables, and to exclude a non-constant Variable from automatic differentiation (TODO document that usage).

Additional attributes can be specified to provide model-specific information, with defaults defined in the Reaction .jl code that can often then be overridden in the .yaml configuration file, see StandardAttributes. Examples include:

  • :initial_value, :norm_value, :initial_delta for state variables or constant variables. NB: the Reaction creating these variables is responsible for implementing a setup method to read the attributes and set the variable data array appropriately at model initialisation.
  • An :advect attribute is used to label tracer variables to indicate that they should have advective transport applied by a transport Reaction included in the model.

NB: after Variables are linked to Domain Variables, the attributes used are those from the master Variable (either a Property or Target variable, or a labelled state variable Dependency or Contributor with no corresponding Property or Target). Additional attributes must therefore be set on this master Variable.

Specifying links

localname identifies the VariableReaction within the Reaction, and can be used to set variable_attributes: and variable_links: in the .yaml configuration file.

linkreq_domain.linkreq_subdomain.linkreq_name defines the Domain, Subdomain and name for run-time linking to VariableDomain variables.

Arguments

  • VT::VariableType: one of VT_ReactProperty, VT_ReactDependency, VT_ReactContributor, VT_ReactTarget
  • localname::AbstractString: Reaction-local Variable name
  • link_namestr::AbstractString: <linkreq_domain>.[linkreq_subdomain.]linkreq_name. Parsed by parse_variablereaction_namestr to define the requested linking to Domain Variable.
  • linklocal_namestr::AbstractString: <linkreq_domain>.[linkreq_subdomain.]localname. Convenience form to define both localname and requested linking to Domain Variable, for the common case where linkreq_name == localname.
  • units::AbstractString: units ("" if not applicable)
  • description::AbstractString: text describing the variable

Keywords

source
PALEOboxes.parse_variablereaction_namestrFunction
parse_variablereaction_namestr(linkstr) 
    -> (linkreq_domain, linkreq_subdomain, linkreq_name, link_optional)

Parse a linkstr into component parts.

linkstr is of format: [(][<linkreq_domain>.][<linkreq_subdomain>.]<linkreq_name>[)]

  • Optional brackets ( ... ) set link_optional=true
  • linkreq_name may contain %reaction% which will later be substituted with <Reaction name>/

Examples:

julia> PALEOboxes.parse_variablereaction_namestr("foo")  # Common case eg for a property that should be public
("", "", "foo", false)

julia> PALEOboxes.parse_variablereaction_namestr("%reaction%foo")  # Reaction-private by default
("", "", "%reaction%foo", false)

julia> PALEOboxes.parse_variablereaction_namestr("ocean.foo")  # Request link to variable of same name in ocean Domain
("ocean", "", "foo", false)

julia> PALEOboxes.parse_variablereaction_namestr("(ocean.oceansurface.goo)") # Full syntax
("ocean", "oceansurface", "goo", true)
source

Defining collections of VariableReactions

PALEOboxes.AbstractVarListType
AbstractVarList

Variables required by a ReactionMethod methodfn are specified by a Tuple of VarList_xxx <: AbstractVarList, each containing a collection of VariableReaction.

These are then converted (by the create_accessors method) to a corresponding Tuple of collections of views on Domain data arrays , which are then be passed to the ReactionMethod methodfn.

NB: creates and uses a copy of supplied Variables including metadata, so set / modify Variable attributes before creating a VarList.

Implementation

Subtypes of AbstractVarList should implement:

  • a constructor that takes a collection of VariableReactions
  • create_accessors, returning the views on Domain data arrays in a subtype-specific collection.
  • get_variables, returning the collection of VariableReactions (as a flat list).
source
PALEOboxes.VarList_singleType
VarList_single(var; components=false) -> VarList_single

Create a VarList_single describing a single VariableReaction, create_accessors will then return a single accessor.

source
PALEOboxes.VarList_namedtupleType
VarList_namedtuple(varcollection; components=false) -> VarList_namedtuple

Create a VarList_namedtuple describing a collection of VariableReactions, create_accessors will then return a NamedTuple with field names = VariableReaction.localname and field values = corresponding data arrays.

If components = true, each NamedTuple field will be a Vector of data array components.

source
PALEOboxes.VarList_tupleType
VarList_tuple(varcollection; components=false) -> VarList_tuple

Create a VarList_tuple describing a collection of VariableReactions, create_accessors will then return a Tuple of data arrays.

An entry of nothing in varcollection will produce nothing in the corresponding entry in the Tuple of arrays supplied to the Reaction method.

If components = true, each Tuple field will be a Vector of data array components.

source
PALEOboxes.VarList_ttupleType
VarList_ttuple(varcollection) -> VarList_ttuple

Create a VarList_ttuple describing a collection of collections of VariableReactions, create_accessors will then return a Tuple of Tuples of data arrays.

source
PALEOboxes.VarList_vectorType
VarList_vector(varcollection; components=false, forceview=false) -> VarList_vector

Create a VarList_vector describing a collection of VariableReactions, create_accessors will then return a Vector of data arrays.

If components = true, each Vector element will be a Vector of data array components.

If forceview = true, each accessor will be a 1-D view to help type stability, even if this is redundant (ie no view required, v::Vector -> view(v, 1:length(v)))

source
PALEOboxes.VarList_vvectorType
VarList_vvector(Vector{Vector{VariableReaction}}::vars; components=false) -> VarList_vvector

Create a VarList_vvector describing a Vector of Vectors of VariableReactions, create_accessors will then return a Vector of Vectors of data arrays.

If components = true, each Vector of Vectors element will be a Vector of data array components.

source
PALEOboxes.VarList_nothingType
VarList_nothing() -> VarList_nothing

Create a placeholder for a missing/unavailable VariableReaction. create_accessors will then return nothing.

source

Implementing method functions

Reaction method functions should iterate over the cells in the Domain supplied by cellrange argument and calculate appropriate biogeochemical fluxes etc (which may include the model time derivative and any intermediate or diagnostic output).

Iterating over cells

The simplest case is a method function that iterates over individual cells, with skeleton form:

function do_something_cellwise(m::PB.AbstractReactionMethod, pars, (vars, ), cellrange::PB.AbstractCellRange, deltat)

    @inbounds for i in cellrange.indices
        vars.A[i]  = something*vars.B[i]*vars.C[i]  # in general A is some function of B, C, etc
        # etc
    end

    return nothing
end

Iterating over cells in columns

If necessary (eg to calculate vertical transport), provided the model grid and cellrange allow, it is possible to iterate over columns and then cells within columns (in order from top to bottom):

function do_something_columnwise(m::PB.AbstractReactionMethod, pars, (vars, ), cellrange::PB.AbstractCellRange, deltat)

    @inbounds for (icol, colindices) in cellrange.columns
        accum = zero(vars.A[first(colindices)]) # accumulator of appropriate type
        for i in colindices  # order is top to bottom
            accum += vars.A[i]
            vars.C[i] = accum  # C = sum of A in cells above                 
            # etc
        end

        vars.floor_C[icol] = vars.C[last(colindices)] # assumes model has a floor domain with one floor cell per column in the interior domain
    end

    return nothing
end

Iteration from bottom to top within a column can be implemented using Iterators.reverse, eg

function do_something_columnwise(m::PB.AbstractReactionMethod, pars, (vars, ), cellrange::PB.AbstractCellRange, deltat)
    @inbounds for (icol, colindices) in cellrange.columns
        colreverse = Iterators.reverse(colindices)
        for i in colreverse  # order is bottom to top
            # etc
        end
    end

    return nothing
end
Note

The method function shouldn't make any assumptions about colindices other than that it is a list of indices ordered from top to bottom in a column. Depending on the grid in use, the indices may not be contiguous, and may not be integers.

Note

The example above made the additional assumption that a floor domain had been defined (containing Variable floor_C) with one floor cell per column. This is determined by the model configuration, and is not true in general.

In rare cases where it is necessary to operate on a Vector representing a quantity for the whole column (rather than just iterate through it), this can be implemented using view, eg

function do_something_columnwise(m::PB.AbstractReactionMethod, pars, (vars, ), cellrange::PB.AbstractCellRange, deltat)
    @inbounds for (icol, colindices) in cellrange.columns
        A_col = view(vars.A, colindices)  # A_col is an AbstractVector with contiguous indices 1:length(colindices)
        B_col = view(vars.B, colindices)  # B_col is an AbstractVector with contiguous indices 1:length(colindices)
        
        # do something that needs a vector of cells for a whole column
    end

    return nothing
end

Optimising loops over cells using explicit SIMD instructions

Reactions with simple loops over cellindices that implement time-consuming per-cell calculations may be optimised by using explicit SIMD instructions.

PALEOboxes.SIMDutils.SIMDIterType
SIMDIter(baseiter, Val{N})
SIMDIter(baseiter, ::Type{SIMD.Vec{N, U}})
SIMDIter(baseiter, ::Val{1}) # scalar fallback
SIMDIter(baseiter, ::Type{U}) where {U <: Real} # scalar fallback

Iterator that takes up to N SIMD elements at a time from baseiter (which should represent indices into a Vector). See Julia package SIMD.jl

If baseiter contained 1 or more but less then N elements, then indices is filled with repeats of the last available element.

Returns Tuple of indices (length N).

Examples

v_a = [1.0, 2.0, 3.0, 4.0, 5.0]
v_b = similar(v_a)

iter = eachindex(v_a) # iter should represent indices into a Vector

# simplest version - Float64 x 4, ie type of v_a x 4

for i in SIMDIter(iter, Val(4))
    x = v_a[i]  # x is a packed SIMD vector
    v_b[i] = x
end


# with type conversion - Float32 x 8, ie explicitly change Type of SIMD vector

ST = SIMD.Vec{8, Float32}}    
for i in SIMDIter(iter, ST)
    #   v = vec[i]  <--> vgatherind(ST, vec, i)
    #   vec[i] = v  <--> vscatterind!(v, vec, i)
    #   vec[i] += v <--> vaddind!(v, vec, i) 

    x = vgatherind(ST, v_a, i)  # x is a packed SIMD vector with type conversion to Float32
    vscatterind!(x, v_b, i)
end
source

Adding ReactionMethods

PALEOboxes.ReactionMethodType
ReactionMethod(
    methodfn::Function,
    reaction::AbstractReaction,
    name::String,
    varlists::Tuple{Vararg{AbstractVarList}},
    p, 
    operatorID::Vector{Int64}, 
    domain::AbstractDomain; 
    preparefn = (m, vardata) -> vardata
) -> m::ReactionMethod

Defines a callback function methodfn with Variables varlists, to be called from the Model framework either during setup or as part of the main loop.

Fields

  • methodfn: callback from Model framework

  • reaction: the Reaction that created this ReactionMethod

  • name: a descriptive name, eg generated from the name of methodfn

  • varlists: Tuple of VarLists, each representing a list of VariableReactions. Corresponding Variable accessors vardata (views on Arrays) will be provided to the methodfn callback. NB: not concretely typed to reduce compile time, as not performance-critical

  • p: optional context field (of arbitrary type) to store data needed by methodfn.

  • operatorID

  • domain

  • preparefn: preparefn(m::ReactionMethod, vardata::Tuple) -> modified_vardata::Tuple optionally modify vardata to eg add buffers. NB: not concretely typed as not performance-critical

methodfn

The methodfn callback is:

methodfn(m::ReactionMethod, pars, vardata::Tuple, cellrange::AbstractCellRange, modelctxt)

or (if Parameters are not required):

methodfn(m::ReactionMethod, vardata::Tuple, cellrange::AbstractCellRange, modelctxt)

With arguments:

  • m::ReactionMethod: context is available as m.reaction::AbstractReaction (the Reaction that defined the ReactionMethod), and m.p (an arbitrary extra context field supplied when ReactionMethod created).
  • pars: a struct with Parameters as fields (current just the ParametersTuple defined as reaction.pars)
  • vardata: A Tuple of collections of views on Domain data arrays corresponding to VariableReactions defined by varlists
  • cellrange::AbstractCellRange: range of cells to calculate.
  • modelctxt:
    • for a setup method, :setup, :initial_value or :norm_value defining the type of setup requested
    • for a main loop method deltat providing timestep information eg for rate throttling.

preparefn

An optional preparefn callback can be supplied eg to allocate buffers that require knowledge of the data types of vardata or to cache expensive calculations:

preparefn(m::ReactionMethod, vardata::Tuple) -> modified_vardata::Tuple

This is called after model arrays are allocated, and prior to setup.

source
PALEOboxes.add_method_setup!Function
add_method_setup!(reaction::AbstractReaction, method::AbstractReactionMethod)
add_method_setup!(reaction::AbstractReaction, methodfn::Function, vars::Tuple{Vararg{AbstractVarList}}; kwargs...) -> ReactionMethod

Add or create-and-add a setup method (called before main loop) eg to set persistent data or initialize state variables. methodfn, vars, kwargs are passed to ReactionMethod.

source
PALEOboxes.add_method_initialize!Function
add_method_initialize!(reaction::AbstractReaction, method::AbstractReactionMethod)
add_method_initialize!(reaction::AbstractReaction, methodfn::Function, vars::Tuple{Vararg{AbstractVarList}}; kwargs...) -> ReactionMethod

Add or create-and-add an initialize method (called at start of each main loop iteration) eg to zero out accumulator Variables. methodfn, vars, kwargs are passed to ReactionMethod.

source
PALEOboxes.add_method_do!Function
add_method_do!(reaction::AbstractReaction, method::AbstractReactionMethod)
add_method_do!(reaction::AbstractReaction, methodfn::Function, vars::Tuple{Vararg{AbstractVarList}}; kwargs...) -> ReactionMethod

Add or create and add a main loop method. methodfn, vars, kwargs are passed to ReactionMethod.

source

Predefined ReactionMethods

Setup and initialization of Variables

PALEOboxes.add_method_setup_initialvalue_vars_default!Function
add_method_setup_initialvalue_vars_default!(react::AbstractReaction, variables [; kwargs...])

Create and add a default method to initialize Variables matching filterfn (defaults to state Variables) at beginning of integration.

Setup callbacks used

  • State Variables and similar (:vfunction != VF_Undefined) are initialized in a setup callback with attribute_name in (:initial_value, :norm_value), with values from those Variable attributes.
  • If force_state_norm_value=false, other Variables (with :vfunction == VF_Undefined) are initialized in a setup callback with attribute_name=:setup, with values from the :initial_value Variable attribute. NB: filterfn must be set to include these Variables.
  • If force_initial_norm_value=true, all Variables (including those with :vfunction == VF_Undefined) are initialised as state Variables

Keywords

  • filterfn: set to f(var)::Bool to override the default selection for state variables only (Variables with :vfunction in (VF_StateExplicit, VF_State, VF_Total, VF_StateTotal, VF_Constraint))
  • force_initial_norm_value=false: true to always use :initial_value, :norm_value, even for variables with :vfunction=VF_Undefined
  • transfer_attribute_vars=[]: Set to a list of the same length as variables to initialise variables from attributes of transfer_attribute_vars.
  • setup_callback=(method, attribute_name, var, vardata) -> nothing: Set to a function that is called after each Variable initialisation eg to store norm values.
  • convertvars=[]
  • convertfn = (convertvars_tuple, i) -> 1.0
  • convertinfo = ""

Including volume etc conversion

Set convertvars to a Vector of Variables (eg for cell volume) and supply convertfn and convertinfo to initialize to :initial_value*convertfn(convertvars_tuple, i) where the argument of convertfn is the Tuple generated by VarList_tuple(convertvars).

Example: To interpret :initial_value as a concentration-like quantity:

convertvars = [volume], 
convertfn = ((volume, ), i) -> volume[i], 
convertinfo = " * volume"
source
PALEOboxes.add_method_initialize_zero_vars_default!Function
add_method_initialize_zero_vars_default!(react::AbstractReaction, variables=PB.get_variables(react))

Create and add a default method to initialize Variables to zero at beginning of each timestep. Defaults to adding all Variables from react with :initialize_to_zero attribute true.

NB: TODO variables are converted to VarDep (no dependency checking or sorting needed, could define a VarInit or similar?)

source

Adding totals for Variables

PALEOboxes.add_method_do_totals_default!Function
add_method_do_totals_default!(react::AbstractReaction, total_candidates=PB.get_variables(react);
    [filterfn] [, methodname] [, total_localnames] [, operatorID])

Create and add a method to add total variables (Scalar Properties), for Variables in collection total_candidates that match filterfn (defaults to those that are Array Variables and have attribute `:calc_total == true).

NB: total Variables will require initialization to zero using add_method_initialize_zero_vars_default!

source

Chemical reactions

PALEOboxes.RateStoichType
RateStoich(
    ratevartemplate, stoich_statevarname;
    deltavarname_eta=nothing, prcessname="", sms_prefix="", sms_suffix="_sms"
) -> RateStoich

Calculate fluxes for a biogeochemical reaction given rate, stoichiometry, and optionally isotope eta.

Add to a Reaction using create_ratestoich_method and add_method_do!.

A Property Variable should be set to provide the reaction rate (often this is implemented by another method of the same Reaction). This method will then link to that (using the local and link names supplied by ratevartemplate) and calculate the appropriate product rates, omitting products that are not present (VariableReaction not linked) in the Model configuration. Metadata for use when analysing model output should be added to the rate variable using add_rate_stoichiometry!, in the usual case where this Variable is supplied as ratevartemplate this will happen automatically.

Arguments:

  • ratevartemplate::Union{VarPropT, VarDepT}: used to define the rate variable local and link names.
  • stoich_statevarname: collection of Tuple(stoichiometry, name) eg ((-2.0, "O2"), (-1.0,"H2S::Isotope"), (1.0, "SO4::Isotope"))
  • deltavarname_eta: optional tuple of variable delta + eta ("SO4_delta", -30.0) or ("SO4_delta", rj.pars.delta). If a Parameter is supplied, this is read in do_react_ratestoich to allow modification.
  • processname::String: optional tag to identify the type of reaction in model output
  • add_rate_stoichiometry=true: true to add call add_rate_stoichiometry! to add metadata to ratevartemplate.

Examples:

Create a RateStoich representing the reaction 2 O2 + H2S -> H2SO4

julia> myratevar = PALEOboxes.VarProp("myrate", "mol yr-1", "a rate");

julia> rs = PALEOboxes.RateStoich(myratevar, ((-2.0, "O2"), (-1.0,"H2S"), (1.0, "SO4")));

julia> rs.stoich_statevarname
((-2.0, "O2"), (-1.0, "H2S"), (1.0, "SO4"))
source
PALEOboxes.add_rate_stoichiometry!Function
add_rate_stoichiometry!(ratevar::VarPropT, ratestoich::RateStoich)

Add metadata to rate variable ratevar for use when analysing model output.

Only needs to be called explicitly if RateStoich was supplied with a VarDep that links to the rate variable, not the rate variable itself.

Adds Variable attributes:

  • rate_processname::String = ratestoich.processname
  • rate_species::Vector{String} reactants + products from ratestoich.stoich_statevarname
  • rate_stoichiometry::Vector{Float64} reaction stoichiometry from ratestoich.stoich_statevarname
source

Internal details of Variable arrays accessor generation

VariableReactions in the AbstractVarLists for a ReactionMethod are processed by create_accessor to supply views on arrays as corresponding arguments to the ReactionMethod function.

PALEOboxes.create_accessorFunction
 create_accessor(var::VariableReaction, modeldata, arrays_idx, components, [,forceview=false])
    -> accessor or (accessor, subdomain_indices)

Creates a view on a (single) VariableDomain data array linked by var::VariableReaction. Called by an AbstractVarList create_accessors implementation to generate a collection of views for multiple VariableReactions.

Returns:

  • if var is linked, an accessor or Tuple (accessor, subdomain_indices) that provides a view on variable data.
  • if var is not linked, nothing if var is optional, or errors and doesn't return if var is non-optional.

Mapping of indices for Subdomains <–> Domains:

  • if no Subdomain, returns unmodified indices (if forceview=false), or an equivalent view (if forceview=true, this is to help type stability)
  • if Variable is a Domain interior and Subdomain is a Domain boundary, accessor is a view with a subset of Domain indices.
  • if Variable is a Domain boundary and Subdomain is the Domain interior, returns a Tuple with subdomain_indices, length(Subdomain size), with missing for interior points.

Mapping of multi-component (Isotope) Variables:

  • If components=false:
    • map multi-component Variable to accessor::IsotopeArray
    • return a single-component Variable as a accessor::AbstractArray.
  • If components=true:
    • variable data as a accessor::Vector{Array}, length=number of components
source
PALEOboxes.create_accessorsFunction
create_accessors(varlist::AbstractVarList, modeldata::AbstractModelData, arrays_idx::Int) -> vardata

Return a collection vardata of views on Domain data arrays for VariableReactions in varlist. Collection and view are determined by varlist Type.

source